/*
 * Copyright 2011 Angel Sanadinov
 *
 * This file is part of VBox WMI.
 *
 * VBox WMI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VBox WMI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VBox WMI.  If not, see <http://www.gnu.org/licenses/>.
 */

package Services;

import Beans.Requests.AuthorizationRequest;
import Beans.Requests.MachineManagement.ChangeMachineExecutionStateRequest;
import Beans.Requests.MachineManagement.Components.AudioSettings;
import Beans.Requests.MachineManagement.Components.BIOSSettings;
import Beans.Requests.MachineManagement.Components.BootSettings;
import Beans.Requests.MachineManagement.Components.CPUSettings;
import Beans.Requests.MachineManagement.Components.ControllerSettings;
import Beans.Requests.MachineManagement.Components.DeviceSettings;
import Beans.Requests.MachineManagement.Components.IOSettings;
import Beans.Requests.MachineManagement.Components.MediumMountSettings;
import Beans.Requests.MachineManagement.Components.MemorySettings;
import Beans.Requests.MachineManagement.Components.NATEngineForwardingRule;
import Beans.Requests.MachineManagement.Components.NATEngineSettings;
import Beans.Requests.MachineManagement.Components.NetworkAdapterLiveSettings;
import Beans.Requests.MachineManagement.Components.NetworkAdapterSettings;
import Beans.Requests.MachineManagement.Components.SharedFolderSettings;
import Beans.Requests.MachineManagement.Components.StorageSettings;
import Beans.Requests.MachineManagement.Components.VRDPSettings;
import Beans.Requests.MachineManagement.Components.VideoSettings;
import Beans.Requests.MachineManagement.CreateMachineRequest;
import Beans.Requests.MachineManagement.MachineManagementRequest;
import Beans.Requests.MachineManagement.ManageMachineSnapshotsRequest;
import Beans.Requests.MachineManagement.ModifyMachineAttributesRequest;
import Beans.Requests.MachineManagement.ModifyMachineLiveSettingsRequest;
import Beans.Requests.MachineManagement.ModifyMachineStorageRequest;
import Beans.Requests.MachineManagement.MountMediumRequest;
import Beans.Requests.MachineManagement.RemoveMachineRequest;
import Beans.Requests.MediumManagement.MediumManagementRequest;
import Beans.Requests.RequestBean;
import Beans.Requests.SimpleAuthorizationRequest;
import Beans.Responses.MachineManagementResponse;
import Beans.Responses.MediumManagementResponse;
import Beans.VirtualResourceBean;
import Managers.DatabaseManager;
import Managers.LogsManager;
import Managers.PermissionsManager;
import Managers.VBoxConnectionsManager;
import Managers.Utilities.DatabaseManipulationResult;
import Managers.Utilities.RequestResponseWrapper;
import Managers.Utilities.ResponseMessagesContainer;
import Utilities.ApplicationException;
import Utilities.Constants.VirtualResourceType;
import Utilities.Constants;
import Utilities.ExceptionMessages;
import Utilities.Constants.MachineRemoteSessionType;
import Utilities.Constants.StorageDeviceAction;
import Utilities.Functions;
import Utilities.ParameterNames;
import Utilities.PermissionsConstants;
import com.sun.xml.ws.commons.virtualbox_3_2.IConsole;
import com.sun.xml.ws.commons.virtualbox_3_2.IHost;
import com.sun.xml.ws.commons.virtualbox_3_2.IMachine;
import com.sun.xml.ws.commons.virtualbox_3_2.IMediumAttachment;
import com.sun.xml.ws.commons.virtualbox_3_2.INATEngine;
import com.sun.xml.ws.commons.virtualbox_3_2.INetworkAdapter;
import com.sun.xml.ws.commons.virtualbox_3_2.IProgress;
import com.sun.xml.ws.commons.virtualbox_3_2.ISession;
import com.sun.xml.ws.commons.virtualbox_3_2.ISnapshot;
import com.sun.xml.ws.commons.virtualbox_3_2.IStorageController;
import com.sun.xml.ws.commons.virtualbox_3_2.ISystemProperties;
import com.sun.xml.ws.commons.virtualbox_3_2.IVRDPServer;
import com.sun.xml.ws.commons.virtualbox_3_2.IVirtualBox;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceException;
import org.virtualbox_3_2.DeviceType;
import org.virtualbox_3_2.MachineState;
import org.virtualbox_3_2.NetworkAttachmentType;
import org.virtualbox_3_2.SessionState;

/**
 * This servlet represents the application's Machines Management Service. <br><br>
 *
 * This service takes care of the machines creation, removal and modification
 * (changing online/offline attributes, managing snapshots, etc).
 *
 * @author Angel Sanadinov
 */
public class MachinesManagementService extends HttpServlet
{
    private DatabaseManager databaseManager;             //database manager
    private LogsManager logsManager;                     //logs manager
    private PermissionsManager permissionsManager;       //permissions manager
    private VBoxConnectionsManager connectionsManager;   //virtualbox connections manager
    private int maxOperationWaitTime; //max time in ms to wait for VirtualBox operation completion
    private MachineRemoteSessionType sessionType; //type of remote session to be initiated

    /**
     * Initializes the service and retrieves all necessary managers.
     *
     * @throws ServletException if an exception occurs that interrupts the servlet's
     *                          normal operation
     *
     * @see HttpServlet#init()
     */
    @Override
    public void init() throws ServletException
    {
        ServletContext context = getServletContext();
        databaseManager = (DatabaseManager)context.getAttribute(Constants.CONTEXT_DATABASE_MANAGER);
        logsManager = (LogsManager)context.getAttribute(Constants.CONTEXT_LOGS_MANAGER);
        permissionsManager = (PermissionsManager)context.getAttribute(Constants.CONTEXT_PERMISSIONS_MANAGER);
        connectionsManager = (VBoxConnectionsManager)context.getAttribute(Constants.CONTEXT_VBOX_CONNECTIONS_MANAGER);

        maxOperationWaitTime = 0;
        try
        {
            maxOperationWaitTime =
                    Integer.parseInt(context.getInitParameter(ParameterNames.MAX_OPERATION_WAIT_TIME));
        }
        catch(NumberFormatException e)
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_WARNING,
                                       "Machines Management Service: Failed to parse initialization parameter."
                                       + " Default value (" + maxOperationWaitTime
                                       + ") for maxOperationWaitTime will be used.");
        }

        sessionType = MachineRemoteSessionType.VRDP;
        try
        {
            sessionType = MachineRemoteSessionType.valueOf(
                    context.getInitParameter(ParameterNames.REMOTE_SESSION_TYPE));
        }
        catch(Exception e)
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_WARNING,
                                       "Machines Management Service: Failed to parse initialization parameter."
                                       + " Default value (" + sessionType
                                       + ") for sessionType will be used.");
        }
    }

    /**
     * Shuts down the service and cleans up any object references.
     *
     * @see HttpServlet#destroy()
     */
    @Override
    public void destroy()
    {
        databaseManager = null;
        logsManager = null;
        permissionsManager = null;
        connectionsManager = null;
    }

    /**
     * Processes a <code>GET</code> request from a client.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request for the GET could not be handled
     * @throws IOException if an input or output error is detected when the servlet
     *         handles the GET request
     *
     * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Processes a <code>POST</code> request from a client.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request for the POST could not be handled
     * @throws IOException if an input or output error is detected when the servlet
     *         handles the POST request
     *
     * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Processes all <code>GET</code> and <code>POST</code> requests and carries out
     * operations based on requested actions.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request could not be handled
     * @throws IOException if an input or output error is detected when the service
     *         handles the request
     */
    private void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        //retrieves the machine management request
        MachineManagementRequest machineRequest =
                (MachineManagementRequest)request.getAttribute(ParameterNames.REQUEST_MANAGE_MACHINE);

        //checks that the request is available and is valid
        if(machineRequest != null && machineRequest.isValid())
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                           "Request: " + machineRequest.toString());
            
            //attempts to authorize the request
            boolean isActionAuthorized = false;
            //if a new machine is to be created, no UUID is available...
            if(machineRequest.getRequestedAction() == PermissionsConstants.ACTION_MACHINE_CREATE)
            {
                    isActionAuthorized = permissionsManager.isActionAuthorized(
                                            new SimpleAuthorizationRequest(machineRequest.getRequestorId(),
                                                                           machineRequest.getServerId(),
                                                                           machineRequest.getRequestedAction(),
                                                                           VirtualResourceType.MACHINE));
            }
            else //all other request should have machine UUIDs
            {
                    isActionAuthorized = permissionsManager.isActionAuthorized(
                                                new AuthorizationRequest(machineRequest.getRequestorId(),
                                                                         machineRequest.getServerId(),
                                                                         machineRequest.getRequestedAction(),
                                                                         VirtualResourceType.MACHINE,
                                                                         machineRequest.getMachineId()));
            }

            //<editor-fold defaultstate="collapsed" desc="Action Selection">
            //checks if the request is authorized...
            if(isActionAuthorized)
            {
                //carries out the appropriate action based on the request data
                switch(machineRequest.getRequestedAction())
                {
                    //a new virtual machine is to be created
                    case PermissionsConstants.ACTION_MACHINE_CREATE:
                    {
                        processResponse(request, createVirtualMachine((CreateMachineRequest)machineRequest, request, response));
                    } break;

                    //an existing virtual machine is to be removed
                    case PermissionsConstants.ACTION_MACHINE_REMOVE:
                    {
                        processResponse(request, removeVirtualMachine((RemoveMachineRequest)machineRequest));
                    } break;

                    //the attributes of an existing virtual machine are to be modified
                    case PermissionsConstants.ACTION_MACHINE_MODIFY_ATTRIBUTES:
                    {
                        processResponse(request, modifyVirtualMachineAttributes((ModifyMachineAttributesRequest)machineRequest));
                    } break;

                    //the storage settings of a virtual machine are to be modified
                    case PermissionsConstants.ACTION_MACHINE_MODIFY_STORAGE:
                    {
                        processResponse(request, modifyVirtualMachineStorage((ModifyMachineStorageRequest)machineRequest, request, response));
                    } break;

                    //the attributes of a (potentially) running virtual machine are to be modified
                    case PermissionsConstants.ACTION_MACHINE_MODIFY_LIVE:
                    {
                        processResponse(request, modifyVirtualMachineLive((ModifyMachineLiveSettingsRequest)machineRequest));
                    } break;

                    //mounts/unmounts a medium
                    case PermissionsConstants.ACTION_MACHINE_MOUNT_MEDIUM:
                    {
                        processResponse(request, modifyVirtualMachineMountMedium((MountMediumRequest)machineRequest));
                    } break;

                    //changes the running state of a virtual machine
                    case PermissionsConstants.ACTION_MACHINE_CHANGE_STATE:
                    {
                        processResponse(request, changeVirtualMachineState((ChangeMachineExecutionStateRequest)machineRequest));
                    } break;

                    //manages a virtual machine's snapshots
                    case PermissionsConstants.ACTION_MACHINE_SNAPSHOT_MANAGE:
                    {
                        processResponse(request, manageVirtualMachineSnapshots((ManageMachineSnapshotsRequest)machineRequest));
                    } break;

                    default:
                    {
                        processResponse(request, new MachineManagementResponse(false, 
                                new ApplicationException(ExceptionMessages.APP_E_INVALID_REQUESTED_ACTION)));
                    } break;
                }
            }
            else
            {
                processResponse(request, new MachineManagementResponse(false, 
                        new ApplicationException(ExceptionMessages.APP_E_OPERATION_NOT_ALLOWED)));
            }
            //</editor-fold>
        }
        else
        {
            processResponse(request, new MachineManagementResponse(false, 
                    new ApplicationException(ExceptionMessages.APP_E_INVALID_REQUEST)));
        }
    }

    /**
     * Processes all responses going out of the service.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @param machineResponse machine management response object
     * @throws ServletException if the processing of the response could not be handled
     * @throws IOException if an input or output error is detected when the response
     *                     is handled
     */
    private void processResponse(HttpServletRequest request, MachineManagementResponse machineResponse)
            throws ServletException, IOException
    {
        //retrieves the request object from the session
        RequestBean requestData = (RequestBean)request.getAttribute(ParameterNames.REQUEST_MANAGE_MACHINE);
        request.removeAttribute(ParameterNames.REQUEST_MANAGE_MACHINE);
        request.setAttribute(ParameterNames.RESPONSE_MANAGE_MACHINE, machineResponse);

        //checks if the requested action failed
        if(!machineResponse.getRequestResult() ||
                (machineResponse.getExceptions() != null && !machineResponse.getExceptions().isEmpty()))
        {
            //the request failed; retrieves the message container and stores the request/response pair
            ResponseMessagesContainer messagesContainer = (ResponseMessagesContainer)
                    request.getSession(false).getAttribute(ParameterNames.SESSION_REQUEST_MESSAGES_OBJECT);

            messagesContainer.put(new RequestResponseWrapper(requestData, machineResponse));

            if(machineResponse.getExceptions() == null)
            {
                logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                           "Request execution failed: "
                                           + machineResponse.getException().toString());
            }
            else
            {
                for(ApplicationException exception : machineResponse.getExceptions())
                {
                    logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                       "Request execution failed: " + exception.toString());
                }
            }
        }
        else
            ;
    }

    /**
     * Creates a new virtual machine.
     *
     * @param request machine creation request
     * @param servletRequest HttpServletRequest (for sending media management requests)
     * @param servletResponse HttpServletResponse (for receiving responses about the media requests)
     *
     * @return a response to the machine management request
     *
     * @throws ServletException if the processing of the request could not be handled
     * @throws IOException if an input or output error is detected when the request
     *                     is handled
     */
    private MachineManagementResponse createVirtualMachine(CreateMachineRequest request, 
                                                           HttpServletRequest servletRequest,
                                                           HttpServletResponse servletResponse)
            throws ServletException, IOException
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;
        ArrayList<MediumManagementResponse> storageResponses = new ArrayList<MediumManagementResponse>();

        //retrieves the new machine's storage settings
        StorageSettings storageSetup = request.getStorageSettings();
        if(storageSetup != null) //checks if storage setup is needed
        {
            //retrieves a request dispatcher for the media management service
            RequestDispatcher dispatcher = getServletContext().getNamedDispatcher("MediaManagementService");

            if(dispatcher != null)
            {
                //goes through each available medium request and sends it to the media service
                for(MediumManagementRequest mediumRequest : storageSetup.getMediaRequests())
                {
                    if(mediumRequest != null && mediumRequest.getRequestorId() == request.getRequestorId())
                    {
                        //no need for auth checks here, the media management service will take care of that
                        servletRequest.setAttribute(ParameterNames.REQUEST_MANAGE_MEDIUM, mediumRequest);
                        dispatcher.include(servletRequest, servletResponse);

                        //retrieves and saves the storage response
                        storageResponses.add((MediumManagementResponse)servletRequest.getAttribute(ParameterNames.RESPONSE_MANAGE_MEDIUM));
                    }
                    else
                        storageResponses.add(null);
                }

                passedPreOp = true;
            }
            else
            {
                preOpException = new ApplicationException(ExceptionMessages.APP_E_DISPATCHER_RETRIEVAL_FAILED, 
                                                          "Media Management");
            }
        }
        else //no storage setup is required
            passedPreOp = true;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox == null) //checks if the virtualbox object was retrieved
        {
            passedPreOp = false;
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);
        }
        else
            ;

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();

        IMachine newMachine = null;
        String newMachineUUID = null;

        try
        {
            //creates the virtual machine object
            newMachine = vbox.createMachine(request.getMachineName(), request.getOsTypeId(), null, null, false);
            newMachine.setDescription(request.getMachineDescription()); //sets the machine description
            
            //Setting up of the various virtual machine components

            //<editor-fold defaultstate="collapsed" desc="Audio Settings">
            //checks if the audio configuration is to be set
            AudioSettings audioSetup = request.getAudioSettings();
            if(audioSetup != null)
            {
                newMachine.getAudioAdapter().setAudioController(audioSetup.getControllerType());
                newMachine.getAudioAdapter().setAudioDriver(audioSetup.getDriverType());
                newMachine.getAudioAdapter().setEnabled(audioSetup.getAdapterState());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="BIOS Settings">
            //checks if the BIOS configuration is to be set
            BIOSSettings biosSetup = request.getBiosSettings();
            if(biosSetup != null)
            {
                newMachine.getBIOSSettings().setACPIEnabled(biosSetup.getIsACPIEnabled());
                newMachine.getBIOSSettings().setBootMenuMode(biosSetup.getBootMenuMode());
                newMachine.getBIOSSettings().setIOAPICEnabled(biosSetup.getIsIOAPICEnabled());
                newMachine.getBIOSSettings().setTimeOffset(biosSetup.getHostTimeOffset());

                newMachine.setFirmwareType(biosSetup.getFirmwareType());
                newMachine.setHpetEnabled(biosSetup.getIsHpetEnabled());
                newMachine.setRTCUseUTC(biosSetup.getRunRTCinUTC());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Boot Settings">
            //checks if the boot configuration is to be set
            BootSettings bootSetup = request.getBootSettings();
            if(bootSetup != null)
            {
                DeviceType[] bootDevices = bootSetup.getDevices();
                //sets the boot device for each boot position
                for(int i = 0; i < bootDevices.length; i++)
                {
                    try
                    {
                        newMachine.setBootOrder((long)(i+1), bootDevices[i]);
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="CPU Settings">
            //checks if the CPU configuration is to be set
            CPUSettings cpuSetup = request.getCpuSettings();
            if(cpuSetup != null)
            {
                newMachine.setCPUHotPlugEnabled(cpuSetup.getIsCPUHotPlugEnabled());
                newMachine.setCPUCount((long)cpuSetup.getCPUCount());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="I/O Settings">
            //checks if the I/O configuration is to be set
            IOSettings ioSetup = request.getIoSettings();
            if(ioSetup != null)
            {
                newMachine.setIoCacheEnabled(ioSetup.getCacheStatus());
                newMachine.setIoCacheSize((long)ioSetup.getCacheSize());
                newMachine.setIoBandwidthMax((long)ioSetup.getCacheMaxBandwidth());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Memory Settings">
            //checks if the memory configuration is to be set
            MemorySettings memorySetup = request.getMemorySettings();
            if(memorySetup != null)
            {
                try
                {
                    newMachine.setMemorySize((long)memorySetup.getRamSize());
                    newMachine.setMemoryBalloonSize((long)memorySetup.getRamBalloonSize());
                    newMachine.setPageFusionEnabled(memorySetup.getIsPageFusionEnabled());
                }
                catch(WebServiceException e)
                {
                    opExceptions.add(Functions.parseVirtualBoxException(e));
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Networking Settings">
            //checks if the network adapters configurations are to be set and
            //if the number of adapter configuration is correct
            NetworkAdapterSettings[] networksSetup = request.getNetworksSettings();
            ISystemProperties systemProperties = vbox.getSystemProperties();
            if(networksSetup != null && systemProperties.getNetworkAdapterCount().intValue() == networksSetup.length)
            {
                //sets the proper configuration for each adapter
                for(int i = 0; i < networksSetup.length; i++)
                {
                    //retrieves a machine's network adapter
                    INetworkAdapter machineAdapter = newMachine.getNetworkAdapter(new Long(i));

                    //checks if the configuration is to be made
                    if(networksSetup[i] != null)
                    {
                        machineAdapter.setAdapterType(networksSetup[i].getAdapterType());
                        machineAdapter.setCableConnected(networksSetup[i].getIsCableConnected());
                        machineAdapter.setLineSpeed(new Long(networksSetup[i].getLineSpeed()));
                        machineAdapter.setEnabled(networksSetup[i].getAdapterState());
                        machineAdapter.setMACAddress(networksSetup[i].getMACAddress());
                        machineAdapter.setBootPriority(new Long(networksSetup[i].getBootPriority()));

                        //<editor-fold defaultstate="collapsed" desc="Attachment Authorization">
                        //checks if network attachment is authorized
                        boolean isAttachmentAuthorized = false;
                        switch(networksSetup[i].getAttachmentType())
                        {
                            case BRIDGED:
                            case HOST_ONLY:
                            {
                                try
                                {
                                    String networkName = networksSetup[i].getAttachedNetworkName();
                                    IHost host = vbox.getHost();
                                    String networkId = host.findHostNetworkInterfaceByName(networkName).getId();

                                    AuthorizationRequest permissionsRequest =
                                            new AuthorizationRequest(request.getRequestorId(),
                                                                     request.getServerId(),
                                                                     PermissionsConstants.ACTION_NETWORK_ATTACH,
                                                                     VirtualResourceType.NETWORK,
                                                                     networkId);

                                    isAttachmentAuthorized =
                                            permissionsManager.isActionAuthorized(permissionsRequest);
                                }
                                catch(WebServiceException e)
                                {
                                    opExceptions.add(Functions.parseVirtualBoxException(e));
                                }
                            } break;

                            case INTERNAL:
                            {
                                AuthorizationRequest permissionsRequest =
                                        new AuthorizationRequest(request.getRequestorId(),
                                                                 request.getServerId(),
                                                                 PermissionsConstants.ACTION_NETWORK_ATTACH,
                                                                 VirtualResourceType.NETWORK,
                                                                 networksSetup[i].getAttachedNetworkName());
                                
                                isAttachmentAuthorized =
                                        permissionsManager.isActionAuthorized(permissionsRequest);
                            } break;

                            //its either NAT, VDE or a detachment, so action is authorized
                            default: isAttachmentAuthorized = true; break;
                        }
                        //</editor-fold>

                        //<editor-fold defaultstate="collapsed" desc="Network Attachment">
                        //carries out the network attachment, if allowed
                        if(isAttachmentAuthorized)
                        {
                            //takes the proper attachment actions, based on the attachment type
                            switch(networksSetup[i].getAttachmentType())
                            {
                                //a bridged network is to be attached
                                case BRIDGED:
                                {
                                    machineAdapter.attachToBridgedInterface();
                                    machineAdapter.setHostInterface(networksSetup[i].getAttachedNetworkName());
                                } break;

                                //a host-only network is to be attached
                                case HOST_ONLY:
                                {
                                    machineAdapter.attachToHostOnlyInterface();
                                    machineAdapter.setHostInterface(networksSetup[i].getAttachedNetworkName());
                                } break;

                                //an internal network is to be attached
                                case INTERNAL:
                                {
                                    machineAdapter.attachToInternalNetwork();
                                    machineAdapter.setInternalNetwork(networksSetup[i].getAttachedNetworkName());
                                    //increase the number of network interfaces attached to the internal network
                                    databaseManager.increaseNumberOfInternalNetworkAttachedNICs(
                                            networksSetup[i].getAttachedNetworkName(), request.getServerId());
                                } break;

                                //a NAT network is to be attached
                                case NAT:
                                {
                                    machineAdapter.attachToNAT();
                                    machineAdapter.setNATNetwork(networksSetup[i].getAttachedNetworkName());

                                    //nat engine settings should be changed later?
                                } break;

                                //a VDE network is to be attached
                                case VDE:
                                {
                                    machineAdapter.attachToVDE();
                                    machineAdapter.setVDENetwork(networksSetup[i].getAttachedNetworkName());
                                } break;

                                //the network adapter is to be disabled
                                default:
                                {
                                    machineAdapter.detach(); //detaches any network
                                    machineAdapter.setEnabled(false); //disables the interface
                                } break;
                            }
                        }
                        else
                        {
                            opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_NETWORK_ATTACHMENT_NOT_ALLOWED,
                                                                      "(" + networksSetup[i].getAttachedNetworkName() + ")"));
                        }
                        //</editor-fold>
                    }
                    else //the network adapter is to be disabled
                    {
                        if(machineAdapter.getAttachmentType() == NetworkAttachmentType.INTERNAL)
                        {//there is little to no change that this will happen for machine creation...
                            //decreases the number of network interfaces attached to the internal network
                            databaseManager.decreaseNumberOfInternalNetworkAttachedNICs(
                                    machineAdapter.getInternalNetwork(), request.getServerId());
                        }
                        else
                            ;

                        machineAdapter.detach(); //detaches any network
                        machineAdapter.setEnabled(false); //disables the interface
                    }
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Shared Folders Settings">
            //checks if the shared folders configuration is to be set
            ArrayList<SharedFolderSettings> sharedFolders = request.getSharedFoldersSettings();
            if(sharedFolders != null)
            {
                String sharedFoldersRoot = databaseManager.getServerSharedFoldersRoot(request.getServerId());
                //adds each shared folders
                for(SharedFolderSettings sharedFolder : sharedFolders)
                {
                    try
                    {
                        newMachine.createSharedFolder(sharedFolder.getName(),
                                                      sharedFoldersRoot + sharedFolder.getHostPath(),
                                                      sharedFolder.getIsWritable());
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Storage Settings">
            //checks if the storage configuration is to be set
            if(storageSetup != null)
            {
                ArrayList<ControllerSettings> controllers = storageSetup.getControllersSettings();

                //creates all required storage controllers
                for(ControllerSettings controllerSettings : controllers)
                {
                    try
                    {
                        IStorageController controller =
                                newMachine.addStorageController(controllerSettings.getControllerName(),
                                                                controllerSettings.getConnectionType());

                        controller.setControllerType(controllerSettings.getControllerType());
                        controller.setUseHostIOCache(controllerSettings.getUseHostIOCache());
                        controller.setPortCount(controller.getMaxPortCount());
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="VRDP Settings">
            //checks if the VRDP server configuration is to be set
            VRDPSettings vrdpSetup = request.getVrdpSettings();
            IVRDPServer vrdpServer = newMachine.getVRDPServer();
            vrdpServer.setPorts(Functions.allocateVRDPPorts(databaseManager, request.getServerId()));
            if(vrdpSetup != null)
            {
                vrdpServer.setEnabled(vrdpSetup.getServerState());
                vrdpServer.setNetAddress(vrdpSetup.getServerAddress());
                vrdpServer.setAuthType(vrdpSetup.getAuthenticationType());
                vrdpServer.setAuthTimeout(new Long(vrdpSetup.getAuthenticationTimeout()));
                vrdpServer.setAllowMultiConnection(vrdpSetup.getAllowMultipleConnections());
                vrdpServer.setReuseSingleConnection(vrdpSetup.getReuseSingleConnection());
                vrdpServer.setVideoChannel(vrdpSetup.getRdpVideoChannelState());
                vrdpServer.setVideoChannelQuality(new Long(vrdpSetup.getVideoChannelQuality()));
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Video Settings">
            //checks if the video configuration is to be set
            VideoSettings videoSetup = request.getVideoSettings();
            if(videoSetup != null)
            {
                newMachine.setVRAMSize(new Long(videoSetup.getVramSize()));
                newMachine.setAccelerate2DVideoEnabled(videoSetup.getIs2DAccelerationEnabled());
                newMachine.setAccelerate3DEnabled(videoSetup.getIs3DAccelerationEnabled());
            }
            else
                ;
            //</editor-fold>

            newMachine.saveSettings();          //saves the settings of the new machine
            newMachineUUID = newMachine.getId();//retrieves the machine's UUID
            vbox.registerMachine(newMachine);   //registers the machine in VirtualBox

            //<editor-fold defaultstate="collapsed" desc="Attaching Media">
            //checks if the media attachments configuration is to be set
            if(storageSetup != null)
            {
                ISession machineSession = null;

                try
                {
                    //retrieves and attemtps to open a direct session to the new virtual machine
                    machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());
                    vbox.openSession(machineSession, newMachineUUID);

                    //retrieves the virtual machine object
                    IMachine machine = machineSession.getMachine();

                    //goes through each device/medium that needs to be configured...
                    DeviceSettings[] devicesSetttings = new DeviceSettings[storageSetup.getDevicesSettings().size()];
                    storageSetup.getDevicesSettings().toArray(devicesSetttings);
                    for(int i = 0; i < devicesSetttings.length; i++)
                    {
                        //retrieves the medium's storage response, if available
                        MediumManagementResponse deviceResponse = storageResponses.get(i);

                        //checks if the proper storage response is available
                        if(deviceResponse == null 
                           || (deviceResponse.getRequestResult()
                               && (deviceResponse.getProgress() == null || deviceResponse.getProgress().getCompleted())))
                        {
                            try
                            {
                                String mediumId = null;
                                if(deviceResponse != null && deviceResponse.getMediumId() != null)
                                    mediumId = deviceResponse.getMediumId();
                                else
                                    mediumId = devicesSetttings[i].getMediumId();

                                //checks if the requestor is allowed to attach the medium
                                boolean attachmentAuthorized = 
                                        permissionsManager.isActionAuthorized(
                                            new AuthorizationRequest(request.getRequestorId(),
                                                                     request.getServerId(), 
                                                                     PermissionsConstants.ACTION_MEDIUM_ATTACH, 
                                                                     VirtualResourceType.MEDIUM,
                                                                     mediumId));

                                if(attachmentAuthorized)
                                {
                                    //attaches the medium, based on the supplied configuration
                                    machine.attachDevice(devicesSetttings[i].getControllerName(),
                                                         devicesSetttings[i].getControllerPort(),
                                                         devicesSetttings[i].getControllerSlot(),
                                                         devicesSetttings[i].getDevice(),
                                                         mediumId);
                                }
                                else
                                    opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_MEDIUM_ATTACHMENT_NOT_ALLOWED));
                            }
                            catch(WebServiceException e)
                            {
                                opExceptions.add(Functions.parseVirtualBoxException(e));
                            }
                        }
                        else
                        {
                            opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_STORAGE_ATTACHMENT_FAILED, 
                                                                      "Storage Num. (" + i + ")"));
                        }
                    }

                    machine.saveSettings(); //saves the machine's settings
                    passedOp = true;
                }
                catch(WebServiceException e)
                {
                    opExceptions.add(Functions.parseVirtualBoxException(e));
                }
                finally
                {
                    try
                    {
                        if(machineSession != null)
                            machineSession.close(); //closes the machine session
                        else
                            ;
                    }catch(WebServiceException e){}
                }
            }
            else
                passedOp = true;
            //</editor-fold>
        }
        catch(WebServiceException e)
        {
            opExceptions.add(Functions.parseVirtualBoxException(e));
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;

        //attempts to add the new machine to the database
        DatabaseManipulationResult result =
                databaseManager.insertVirtualResource(new VirtualResourceBean(newMachineUUID,
                                                                             request.getServerId(),
                                                                             request.getRequestorId(),
                                                                             request.getPermissions(),
                                                                             VirtualResourceType.MACHINE));

        if(result.wasOperationSuccessful()) //was the operation successful?
                passedPostOp = true;
        else if(result.getErrorCode() == Constants.SQL_ERROR_ENTRY_EXISTS)
            opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_EXISTS_IN_DB));
        else
        {
            opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                      "(" + result.getErrorCode() + ")"));
        }

        if(!passedPostOp)
            return new MachineManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Removes an existing virtual machine.
     *
     * @param request machine removal request
     * @return a response to the machine management request
     */
    private MachineManagementResponse removeVirtualMachine(RemoveMachineRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox != null) //checks if the virtualbox object was retrieved
        {
            //checks if the virtual machine is to be forcibly removed
            if(request.getForceRemoval())
            {
                try
                {
                    //retrieves a virtual machine session
                    ISession machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());
                    //attempts to retrieve the virtual machine
                    IMachine machine = vbox.getMachine(request.getMachineId());

                    //checks if there are no snapshots and if the session is in the proper state
                    if(machine.getSnapshotCount() == 0
                            && machine.getSessionState() != SessionState.SPAWNING
                            && machine.getSessionState() != SessionState.OPEN)
                    {
                        //opens a direct session with the machine
                        vbox.openSession(machineSession, machine.getId());
                        //retrieves the virtual machine from the session object
                        machine = machineSession.getMachine();

                        //detaches media, if necessary
                        for(IMediumAttachment attachment : machine.getMediumAttachments())
                        {
                            machine.detachDevice(attachment.getController(),
                                                 attachment.getPort(),
                                                 attachment.getDevice());
                        }

                        machine.saveSettings(); //saves the machine settings
                        machineSession.close(); //closes the machine session
                        passedPreOp = true;
                    }
                    else
                        preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_REMOVE_NOT_ALLOWED);
                }
                catch(WebServiceException e)
                {
                    preOpException = Functions.parseVirtualBoxException(e);
                }
            }
            else
                passedPreOp = true;
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;

        IMachine machine = null;

        try
        {
            //unregisters the virtual machine
            machine = vbox.unregisterMachine(request.getMachineId());

            //takes care of any internal networks attached to this machine
            ISystemProperties systemProperties = vbox.getSystemProperties();
            for(int i = 0; i < systemProperties.getNetworkAdapterCount().intValue(); i++)
            {
                INetworkAdapter machineAdapter = machine.getNetworkAdapter(new Long(i));
                if(machineAdapter.getAttachmentType() == NetworkAttachmentType.INTERNAL)
                {
                    //decreases the number of network interfaces attached to the internal network
                    databaseManager.decreaseNumberOfInternalNetworkAttachedNICs(
                            machineAdapter.getInternalNetwork(), request.getServerId());
                }
                else
                    ;
            }

            Functions.freeVRDPPorts(databaseManager, request.getServerId(), machine.getVRDPServer().getPorts());

            //removes the settings file
            machine.deleteSettings();
            passedOp = true;
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //attempts to remove the virtual machine from the database
        DatabaseManipulationResult result =
                databaseManager.removeVirtualResource(machine.getId(), request.getServerId());

        if(result.wasOperationSuccessful()) //was the operation successful?
                passedPostOp = true;
        else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_REMOVED)
            postOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_REMOVAL_FAILED);
        else
            postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION, 
                                                       "(" + result.getErrorCode() + ")");

        if(!passedPostOp)
            return new MachineManagementResponse(false, postOpException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Modifies the offline attributes of a virtual machine.
     *
     * @param request machine attributes modification request
     * @return a response to the machine management request
     */
    private MachineManagementResponse modifyVirtualMachineAttributes(ModifyMachineAttributesRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox != null) //checks if the virtualbox object was retrieved
        {
            try
            {
                //attempts to retrieves the virtual machine
                IMachine machine = vbox.getMachine(request.getMachineId());

                //checks if the machine is in a valid state
                if(machine.getState() == MachineState.ABORTED || machine.getState() == MachineState.POWERED_OFF)
                {
                    //checks if the state of the machine session is valid
                    if(machine.getSessionState() != SessionState.OPEN
                            && machine.getSessionState() != SessionState.SPAWNING)
                        passedPreOp = true;
                    else
                        preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_SESSION_OPEN);
                }
                else
                {
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_IN_INVALID_STATE,
                                                              "(" + machine.getState().value() + ")");
                }
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>
        
        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();

        try
        {
            //retrieves and attempts to open a new direct session with the virtual machine
            ISession machineSession
                    = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());
            vbox.openSession(machineSession, request.getMachineId());

            //retrieves the machine object
            IMachine machine = machineSession.getMachine();

            //<editor-fold defaultstate="collapsed" desc="Machine Name">
            //checks if a new machine name is to be set
            if(request.isMachineNameSet())
            {
                try
                {
                    machine.setName(request.getMachineName());
                }
                catch(WebServiceException e)
                {
                    opExceptions.add(Functions.parseVirtualBoxException(e));
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Machine Description">
            //checks if a new machine description is to be set
            if(request.isMachineDescriptionSet())
                machine.setDescription(request.getMachineDescription()); //sets a new description
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Audio Settings">
            //checks if a new audio configuration is to be set
            AudioSettings audioSetup = request.getAudioSettings();
            if(audioSetup != null)
            {
                machine.getAudioAdapter().setAudioController(audioSetup.getControllerType());
                machine.getAudioAdapter().setAudioDriver(audioSetup.getDriverType());
                machine.getAudioAdapter().setEnabled(audioSetup.getAdapterState());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="BIOS Settings">
            //checks if a new BIOS configuration is to be set
            BIOSSettings biosSetup = request.getBiosSettings();
            if(biosSetup != null)
            {
                machine.getBIOSSettings().setACPIEnabled(biosSetup.getIsACPIEnabled());
                machine.getBIOSSettings().setBootMenuMode(biosSetup.getBootMenuMode());
                machine.getBIOSSettings().setIOAPICEnabled(biosSetup.getIsIOAPICEnabled());
                machine.getBIOSSettings().setTimeOffset(biosSetup.getHostTimeOffset());

                machine.setFirmwareType(biosSetup.getFirmwareType());
                machine.setHpetEnabled(biosSetup.getIsHpetEnabled());
                machine.setRTCUseUTC(biosSetup.getRunRTCinUTC());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Boot Settings">
            //checks if a new boot configuration is to be set
            BootSettings bootSetup = request.getBootSettings();
            if(bootSetup != null)
            {
                //sets the device type for each boot position
                DeviceType[] bootDevices = bootSetup.getDevices();
                for(int i = 0; i < bootDevices.length; i++)
                {
                    try
                    {
                        machine.setBootOrder((long)(i+1), bootDevices[i]);
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="CPU Settings">
            //checks if a new CPU configuration is to be set
            CPUSettings cpuSetup = request.getCpuSettings();
            if(cpuSetup != null)
            {
                try
                {
                    machine.setCPUCount((long)cpuSetup.getCPUCount());
                    machine.setCPUHotPlugEnabled(cpuSetup.getIsCPUHotPlugEnabled());
                }
                catch(WebServiceException e)
                {
                    opExceptions.add(Functions.parseVirtualBoxException(e));
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="I/O Settings">
            //checks if a new I/O configuration is to be set
            IOSettings ioSetup = request.getIoSettings();
            if(ioSetup != null)
            {
                machine.setIoCacheEnabled(ioSetup.getCacheStatus());
                machine.setIoCacheSize((long)ioSetup.getCacheSize());
                machine.setIoBandwidthMax((long)ioSetup.getCacheMaxBandwidth());
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Memory Settings">
            //checks if a new memory configuration is to be set
            MemorySettings memorySetup = request.getMemorySettings();
            if(memorySetup != null)
            {
                try
                {
                    machine.setMemorySize((long)memorySetup.getRamSize());
                    machine.setMemoryBalloonSize((long)memorySetup.getRamBalloonSize());
                    machine.setPageFusionEnabled(memorySetup.getIsPageFusionEnabled());
                }
                catch(WebServiceException e)
                {
                    opExceptions.add(Functions.parseVirtualBoxException(e));
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Networking Settings">
            //checks if the network adapters configurations are to be set and
            //if the number of adapter configuration is correct
            NetworkAdapterSettings[] networksSetup = request.getNetworksSettings();
            ISystemProperties systemProperties = vbox.getSystemProperties();
            if(networksSetup != null && systemProperties.getNetworkAdapterCount().intValue() == networksSetup.length)
            {
                //sets the proper configuration for each adapter
                for(int i = 0; i < networksSetup.length; i++)
                {
                    //retrieves a machine's network adapter
                    INetworkAdapter machineAdapter = machine.getNetworkAdapter(new Long(i));

                    //checks if the configuration is to be made
                    if(networksSetup[i] != null)
                    {
                        machineAdapter.setAdapterType(networksSetup[i].getAdapterType());
                        machineAdapter.setCableConnected(networksSetup[i].getIsCableConnected());
                        machineAdapter.setLineSpeed(new Long(networksSetup[i].getLineSpeed()));
                        machineAdapter.setEnabled(networksSetup[i].getAdapterState());
                        machineAdapter.setMACAddress(networksSetup[i].getMACAddress());
                        machineAdapter.setBootPriority(new Long(networksSetup[i].getBootPriority()));

                        //<editor-fold defaultstate="collapsed" desc="NAT Engine Setup">
                        //checks if the NAT engine configuration is to be set
                        INATEngine natEngine = machineAdapter.getNatDriver();
                        NATEngineSettings natEngineSetup = networksSetup[i].getNatEngineSetup();
                        if(natEngineSetup != null)
                        {
                            //checks if the main engine configuration is to be set
                            if(natEngineSetup.isEngineConfigurationSet())
                            {
                                natEngine.setNetworkSettings(natEngineSetup.getMTU(),
                                                             natEngineSetup.getSocketSendBufferCapacity(),
                                                             natEngineSetup.getSocketReceiveBufferCapacity(),
                                                             natEngineSetup.getInitialTCPWindowSendingSize(),
                                                             natEngineSetup.getInitialTCPWindowReceivingSize());

                                natEngine.setNetwork(natEngineSetup.getNetworkName());
                                natEngine.setHostIP(natEngineSetup.getHostIP());
                            }
                            else
                                ;

                            //checks if the TFTP configuration is to be set
                            if(natEngineSetup.isTFTPConfigurationSet())
                            {
                                natEngine.setTftpPrefix(natEngineSetup.getTftpPrefix());
                                natEngine.setTftpBootFile(natEngineSetup.getTftpBootFile());
                                natEngine.setTftpNextServer(natEngineSetup.getTftpNextServer());
                            }
                            else
                                ;

                            //checks if th DNS configuration is to be set
                            if(natEngineSetup.isDNSConfigurationSet())
                            {
                                natEngine.setDnsPassDomain(natEngineSetup.getDnsPassHostDomain());
                                natEngine.setDnsProxy(natEngineSetup.getDnsProxy());
                                natEngine.setDnsUseHostResolver(natEngineSetup.getDnsUseHostResolver());
                            }
                            else
                                ;

                            //checks if the alias mode is to be set
                            if(natEngineSetup.isAliasModeSet())
                                natEngine.setAliasMode(natEngineSetup.getAliasMode());
                            else
                                ;

                            //checks if there are port forwarding rules for removal
                            if(natEngineSetup.getForwardingRulesForRemoval() != null)
                            {
                                //removes each port forwarding rule
                                for(String ruleForRemoval : natEngineSetup.getForwardingRulesForRemoval())
                                    natEngine.removeRedirect(ruleForRemoval);
                            }
                            else
                                ;

                            //checks if there are port forwarding rules for addition
                            if(natEngineSetup.getNewForwardingRules() != null)
                            {
                                //adds each new port forwarding rule
                                for(NATEngineForwardingRule newRule : natEngineSetup.getNewForwardingRules())
                                {
                                    natEngine.addRedirect(newRule.getName(), newRule.getProtocol(), newRule.getHostIP(),
                                                          newRule.getHostPort(), newRule.getGuestIP(), newRule.getGuestPort());
                                }
                            }
                            else
                                ;
                        }
                        else
                            ;
                        //</editor-fold>

                        //<editor-fold defaultstate="collapsed" desc="Attachment Authorization">
                        //checks if network attachment is authorized
                        boolean isAttachmentAuthorized = false;

                        switch(networksSetup[i].getAttachmentType())
                        {
                            case BRIDGED:
                            case HOST_ONLY:
                            {
                                try
                                {
                                    String networkName = networksSetup[i].getAttachedNetworkName();
                                    IHost host = vbox.getHost();
                                    String networkId = host.findHostNetworkInterfaceByName(networkName).getId();

                                    AuthorizationRequest permissionsRequest =
                                            new AuthorizationRequest(request.getRequestorId(),
                                                                     request.getServerId(),
                                                                     PermissionsConstants.ACTION_NETWORK_ATTACH,
                                                                     VirtualResourceType.NETWORK,
                                                                     networkId);

                                    isAttachmentAuthorized =
                                            permissionsManager.isActionAuthorized(permissionsRequest);
                                }
                                catch(WebServiceException e)
                                {
                                    opExceptions.add(Functions.parseVirtualBoxException(e));
                                }
                            } break;

                            case INTERNAL:
                            {
                                AuthorizationRequest permissionsRequest =
                                        new AuthorizationRequest(request.getRequestorId(),
                                                                 request.getServerId(),
                                                                 PermissionsConstants.ACTION_NETWORK_ATTACH,
                                                                 VirtualResourceType.NETWORK,
                                                                 networksSetup[i].getAttachedNetworkName());

                                isAttachmentAuthorized =
                                        permissionsManager.isActionAuthorized(permissionsRequest);
                            } break;

                            //its either NAT, VDE or a detachment, so action is authorized
                            default: isAttachmentAuthorized = true; break;
                        }
                        //</editor-fold>

                        //<editor-fold defaultstate="collapsed" desc="Network Attachment">
                        //carries out the network attachment, if allowed
                        if(isAttachmentAuthorized)
                        {
                            //checks if the current network is internal
                            if(machineAdapter.getAttachmentType() == NetworkAttachmentType.INTERNAL)
                            {
                                //decreases the number of network interfaces attached to the internal network
                                databaseManager.decreaseNumberOfInternalNetworkAttachedNICs(
                                        machineAdapter.getInternalNetwork(), request.getServerId());
                            }
                            else
                                ;

                            //takes the proper attachment actions, based on the attachment type
                            switch(networksSetup[i].getAttachmentType())
                            {
                                //a bridged network is to be attached
                                case BRIDGED:
                                {
                                    machineAdapter.attachToBridgedInterface();
                                    machineAdapter.setHostInterface(networksSetup[i].getAttachedNetworkName());
                                } break;

                                //a host-only network is to be attached
                                case HOST_ONLY:
                                {
                                    machineAdapter.attachToHostOnlyInterface();
                                    machineAdapter.setHostInterface(networksSetup[i].getAttachedNetworkName());
                                } break;

                                //an internal network is to be attached
                                case INTERNAL:
                                {
                                    machineAdapter.attachToInternalNetwork();
                                    machineAdapter.setInternalNetwork(networksSetup[i].getAttachedNetworkName());
                                    //increase the number of network interfaces attached to the internal network
                                    databaseManager.increaseNumberOfInternalNetworkAttachedNICs(
                                            networksSetup[i].getAttachedNetworkName(), request.getServerId());
                                } break;

                                //a NAT network is to be attached
                                case NAT:
                                {
                                    machineAdapter.attachToNAT();
                                    machineAdapter.setNATNetwork(networksSetup[i].getAttachedNetworkName());

                                    //nat engine settings should be changed later?
                                } break;

                                //a VDE network is to be attached
                                case VDE:
                                {
                                    machineAdapter.attachToVDE();
                                    machineAdapter.setVDENetwork(networksSetup[i].getAttachedNetworkName());
                                } break;

                                //the network adapter is to be disabled
                                default:
                                {
                                    machineAdapter.detach(); //detaches the network
                                    machineAdapter.setEnabled(false); //disables the adapter
                                } break;
                            }
                        }
                        else
                            ;
                        //</editor-fold>
                    }
                    else
                        ;
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="VRDP Settings">
            //checks if a new VRDP server configuration is to be set
            VRDPSettings vrdpSetup = request.getVrdpSettings();
            if(vrdpSetup != null)
            {
                IVRDPServer vrdpServer = machine.getVRDPServer();
                vrdpServer.setEnabled(vrdpSetup.getServerState());
                vrdpServer.setNetAddress(vrdpSetup.getServerAddress());
                vrdpServer.setAuthType(vrdpSetup.getAuthenticationType());
                vrdpServer.setAuthTimeout(new Long(vrdpSetup.getAuthenticationTimeout()));
                vrdpServer.setAllowMultiConnection(vrdpSetup.getAllowMultipleConnections());
                vrdpServer.setReuseSingleConnection(vrdpSetup.getReuseSingleConnection());
                vrdpServer.setVideoChannel(vrdpSetup.getRdpVideoChannelState());
                vrdpServer.setVideoChannelQuality(new Long(vrdpSetup.getVideoChannelQuality()));
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Video Settings">
            //checks if a new video configuration is to be set
            VideoSettings videoSetup = request.getVideoSettings();
            if(videoSetup != null)
            {
                machine.setVRAMSize(new Long(videoSetup.getVramSize()));
                machine.setAccelerate2DVideoEnabled(videoSetup.getIs2DAccelerationEnabled());
                machine.setAccelerate3DEnabled(videoSetup.getIs3DAccelerationEnabled());
            }
            else
                ;
            //</editor-fold>

            machine.saveSettings(); //saves the machine's settings
            machineSession.close(); //closes the machine's sessions

            //<editor-fold defaultstate="collapsed" desc="Updating Permissions">
            //checks if a new permissions are to be set
            if(request.arePermissionsSet())
            {
                //attempts to set the new permissions
                DatabaseManipulationResult result = 
                        databaseManager.updateVirtualResourcePermissions(request.getMachineId(),
                                                                         request.getServerId(),
                                                                         request.getPermissions());

                if(!result.wasOperationSuccessful()) //was the operation successful?
                {
                    if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
                        opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_PERMISSIONS_UPDATE_FAILED));
                    else
                        opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                                  "(" + result.getErrorCode() + ")"));
                }
                else
                    ;
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Changing Owner">
            //checks if a new owner is to be set
            if(request.isNewOwnerIdSet())
            {
                //attempts to set the new owner
                DatabaseManipulationResult result =
                        databaseManager.changeVirtualResourceOwner(request.getMachineId(),
                                                                   request.getServerId(),
                                                                   request.getNewOwnerId());

                if(!result.wasOperationSuccessful()) //was the operation successful?
                {
                    if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
                        opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_OWNER_UPDATE_FAILED));
                    else
                        opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                                  "(" + result.getErrorCode() + ")"));
                }
                else
                    ;
            }
            else
                ;
            //</editor-fold>

            passedOp = true;
        }
        catch(WebServiceException e)
        {
            opExceptions.add(Functions.parseVirtualBoxException(e));
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, opExceptions);
    }

    /**
     * Modifies the storage settings of a virtual machine.
     *
     * @param request machine storage modification request
     * @param servletRequest HttpServletRequest (for sending media management requests)
     * @param servletResponse HttpServletResponse (for receiving responses about the media requests)
     *
     * @return a response to the machine management request
     *
     * @throws ServletException if the processing of the request could not be handled
     * @throws IOException if an input or output error is detected when the request
     *                     is handled
     */
    private MachineManagementResponse modifyVirtualMachineStorage(ModifyMachineStorageRequest request, 
                                                                  HttpServletRequest servletRequest,
                                                                  HttpServletResponse servletResponse)
            throws ServletException, IOException
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;
        ArrayList<MediumManagementResponse> storageResponses = new ArrayList<MediumManagementResponse>();

        //retrieves the storage settings
        StorageSettings storageSetup = request.getStorageSettings();

        //retrieves a dispatcher for the media management service
        RequestDispatcher dispatcher = getServletContext().getNamedDispatcher("MediaManagementService");
        if(dispatcher != null)
        {
            //sends all required media management request to the service
            for(MediumManagementRequest mediumRequest : storageSetup.getMediaRequests())
            {
                //these should be only create disk requests
                if(mediumRequest != null && mediumRequest.getRequestorId() == request.getRequestorId())
                {
                    //no need for auth checks here, the media management service will take care of that :)
                    servletRequest.setAttribute(ParameterNames.REQUEST_MANAGE_MEDIUM, mediumRequest);
                    dispatcher.include(servletRequest, servletResponse);

                    //retrieves and stores the result of the requests
                    storageResponses.add((MediumManagementResponse)servletRequest.getAttribute(ParameterNames.RESPONSE_MANAGE_MEDIUM));
                }
                else
                    storageResponses.add(null);
            }

            passedPreOp = true;
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_DISPATCHER_RETRIEVAL_FAILED, "Media Management");

        //preop stage 1
        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;

        //maybe move this before the storage requests? if vbox reference cannot be retrieved there, possibly
        //every medium request will fail with the same reponse message ?!?
        passedPreOp = false;
        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox != null) //checks if the virtualbox object was retrieved
        {
            try
            {
                //attempts to retrieves the virtual machine
                IMachine machine = vbox.getMachine(request.getMachineId());

                //checks if the machine is in the proper state
                if(machine.getState() == MachineState.ABORTED || machine.getState() == MachineState.POWERED_OFF)
                {
                    //checks if the machine's session is in the proper state
                    if(machine.getSessionState() != SessionState.OPEN
                            && machine.getSessionState() != SessionState.SPAWNING)
                        passedPreOp = true;
                    else
                        preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_SESSION_OPEN);
                }
                else
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_IN_INVALID_STATE,
                                                              "(" + machine.getState().value() + ")");
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        //preop stage 2
        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();
        ISession machineSession = null;

        try
        {
            //retrieves and attempts to open a direct session with the virtual machine
            machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());
            vbox.openSession(machineSession, request.getMachineId());

            //retrieves the machine
            IMachine machine = machineSession.getMachine();

            //checks if there are devices for modification
            ArrayList<DeviceSettings> devices = storageSetup.getDevicesSettings();
            if(devices.size() > 0)
            {
                //<editor-fold defaultstate="collapsed" desc="Detaching devices">
                //detaches all required devices
                for(DeviceSettings device : devices)
                {
                    try
                    {
                        if(device.getDeviceAction() == StorageDeviceAction.DETACH
                           || device.getDeviceAction() == StorageDeviceAction.REPLACE)
                        {
                            machine.detachDevice(device.getControllerName(),
                                                 device.getControllerPort(),
                                                 device.getControllerSlot());
                        }
                        else
                            ;
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
                //</editor-fold>
            }
            else
                ;

            //<editor-fold defaultstate="collapsed" desc="Controllers Management">
            //checks if there are controllers for removal
            ArrayList<String> controllersForRemoval = storageSetup.getControllersForRemoval();
            if(controllersForRemoval.size() > 0)
            {
                for(String controller : controllersForRemoval)
                {
                    try
                    {
                        //removes the controller
                        machine.removeStorageController(controller);
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;

            //checks if there are controllers for addition
            ArrayList<ControllerSettings> controllersForAddition = storageSetup.getControllersSettings();
            if(controllersForAddition.size() > 0)
            {
                //adds and configures each controller
                for(ControllerSettings controller : controllersForAddition)
                {
                    try
                    {
                        IStorageController newController 
                                = machine.addStorageController(controller.getControllerName(), 
                                                               controller.getConnectionType());

                        newController.setControllerType(controller.getControllerType());
                        newController.setUseHostIOCache(controller.getUseHostIOCache());
                        newController.setPortCount(newController.getMaxPortCount());
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;
            //</editor-fold>

            //checks if there are devices for modification
            if(devices.size() > 0)
            {
                //<editor-fold defaultstate="collapsed" desc="Attaching devices">
                //attaches all required devices
                DeviceSettings[] devicesSetttings = new DeviceSettings[storageSetup.getDevicesSettings().size()];
                devices.toArray(devicesSetttings);
                for(int i = 0; i < devicesSetttings.length; i++)
                {
                    //checks if the device is for attachment
                    if(devicesSetttings[i].getDeviceAction() != StorageDeviceAction.ATTACH
                       && devicesSetttings[i].getDeviceAction() != StorageDeviceAction.REPLACE
                       && devicesSetttings[i].getDeviceAction() != StorageDeviceAction.CREATE)
                        continue;
                    else
                        ;

                    //retrieves the medium's storage response, if available
                    MediumManagementResponse deviceResponse = storageResponses.get(i);

                    //checks if the proper storage response is available
                    if(deviceResponse == null
                       || (deviceResponse.getRequestResult()
                           && (deviceResponse.getProgress() == null || deviceResponse.getProgress().getCompleted())))
                    {
                        try
                        {
                            String mediumId = null;
                            if(deviceResponse != null && deviceResponse.getMediumId() != null)
                                mediumId = deviceResponse.getMediumId();
                            else
                                mediumId = devicesSetttings[i].getMediumId();

                            //checks if the requestor is allowed to attach the medium
                            boolean attachmentAuthorized =
                                    permissionsManager.isActionAuthorized(
                                        new AuthorizationRequest(request.getRequestorId(),
                                                                 request.getServerId(),
                                                                 PermissionsConstants.ACTION_MEDIUM_ATTACH,
                                                                 VirtualResourceType.MEDIUM,
                                                                 mediumId));

                            //attaches the medium, if allowed
                            if(attachmentAuthorized)
                            {
                                machine.attachDevice(devicesSetttings[i].getControllerName(),
                                                     devicesSetttings[i].getControllerPort(),
                                                     devicesSetttings[i].getControllerSlot(),
                                                     devicesSetttings[i].getDevice(),
                                                     mediumId);
                            }
                            else
                                opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_MEDIUM_ATTACHMENT_NOT_ALLOWED,
                                                                          "(" + mediumId + ")"));
                        }
                        catch(WebServiceException e)
                        {
                            opExceptions.add(Functions.parseVirtualBoxException(e));
                        }
                    }
                    else
                        opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_MACHINE_STORAGE_ATTACHMENT_FAILED,
                                                                  "Storage Num. (" + i + ")"));
                }
                //</editor-fold>
            }
            else
                ;

            machine.saveSettings(); //saves the machine settings
            passedOp = true;  
        }
        catch(WebServiceException e)
        {
            opExceptions.add(Functions.parseVirtualBoxException(e));
        }
        finally
        {
            try
            {
                if(machineSession != null)
                    machineSession.close(); //closes the machine sessions
                else
                    ;
            }catch(WebServiceException e){}
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, opExceptions);
    }

    /**
     * Modifies the live settings of a virtual machine. <br><br>
     *
     * Can be used both when a machine is online and offline.
     *
     * @param request live machine attributes modification request
     * @return a response to the machine management request
     */
    private MachineManagementResponse modifyVirtualMachineLive(ModifyMachineLiveSettingsRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;
        boolean openExistingSession = false; //if false -> local session is open, else existing is open

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox != null) //checks if the virtualbox object was retrieved
        {
            try
            {
                //retrieves the virtual machine
                IMachine machine = vbox.getMachine(request.getMachineId());

                //checks the machine session state to determine how to open the session
                SessionState machineSessionState = machine.getSessionState();
                if(machineSessionState == SessionState.OPEN || machineSessionState == SessionState.SPAWNING)
                    openExistingSession = true;
                else
                    openExistingSession = false;

                passedPreOp = true;
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();
        ISession machineSession = null;

        try
        {
            //retrieves the a machine session object
            machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());

            //attempts to open a session
            if(openExistingSession)
                vbox.openExistingSession(machineSession, request.getMachineId());
            else
                vbox.openSession(machineSession, request.getMachineId());

            //retrieves the virtual machine object
            IMachine machine = machineSession.getMachine();

            //<editor-fold defaultstate="collapsed" desc="Networking Settings">
            //checks if the network adapters configurations are to be set and
            //if the number of adapter configuration is correct
            NetworkAdapterLiveSettings[] networksSetup = request.getNetworksSettings();
            ISystemProperties systemProperties = vbox.getSystemProperties();
            if(networksSetup != null && systemProperties.getNetworkAdapterCount().intValue() == networksSetup.length)
            {
                //sets the proper configuration for each adapter
                for(int i = 0; i < networksSetup.length; i++)
                {
                    //retrieves a machine's network adapter
                    INetworkAdapter machineAdapter = machine.getNetworkAdapter(new Long(i));

                    //checks if the configuration is to be made
                    if(networksSetup[i] != null && networksSetup[i].isAdapterModified())
                    {
                        //limited configuration is available
                        machineAdapter.setCableConnected(networksSetup[i].getIsCableConnected());
                        machineAdapter.setLineSpeed(new Long(networksSetup[i].getLineSpeed()));

                        //<editor-fold defaultstate="collapsed" desc="NAT Engine Setup">
                        //checks if the NAT engine configuration is to be set
                        INATEngine natEngine = machineAdapter.getNatDriver();
                        NATEngineSettings natEngineSetup = networksSetup[i].getNatEngineSetup();
                        if(natEngineSetup != null)
                        {
                            //checks if the main engine configuration is to be set
                            if(natEngineSetup.isEngineConfigurationSet())
                            {
                                natEngine.setNetworkSettings(natEngineSetup.getMTU(),
                                                             natEngineSetup.getSocketSendBufferCapacity(),
                                                             natEngineSetup.getSocketReceiveBufferCapacity(),
                                                             natEngineSetup.getInitialTCPWindowSendingSize(),
                                                             natEngineSetup.getInitialTCPWindowReceivingSize());

                                natEngine.setNetwork(natEngineSetup.getNetworkName());
                                natEngine.setHostIP(natEngineSetup.getHostIP());
                            }
                            else
                                ;

                            //checks if the TFTP configuration is to be set
                            if(natEngineSetup.isTFTPConfigurationSet())
                            {
                                natEngine.setTftpPrefix(natEngineSetup.getTftpPrefix());
                                natEngine.setTftpBootFile(natEngineSetup.getTftpBootFile());
                                natEngine.setTftpNextServer(natEngineSetup.getTftpNextServer());
                            }
                            else
                                ;

                            //checks if the DNS configuration is to be set
                            if(natEngineSetup.isDNSConfigurationSet())
                            {
                                natEngine.setDnsPassDomain(natEngineSetup.getDnsPassHostDomain());
                                natEngine.setDnsProxy(natEngineSetup.getDnsProxy());
                                natEngine.setDnsUseHostResolver(natEngineSetup.getDnsUseHostResolver());
                            }
                            else
                                ;

                            //checks if the alias mode is to be set
                            if(natEngineSetup.isAliasModeSet())
                                natEngine.setAliasMode(natEngineSetup.getAliasMode());
                            else
                                ;

                            //checks if there are port forwarding rules for r1emoval
                            if(natEngineSetup.getForwardingRulesForRemoval() != null)
                            {
                                //removes each port forwarding rule
                                for(String ruleForRemoval : natEngineSetup.getForwardingRulesForRemoval())
                                    natEngine.removeRedirect(ruleForRemoval);
                            }
                            else
                                ;

                            //checks if there are port forwarding rules for addition
                            if(natEngineSetup.getNewForwardingRules() != null)
                            {
                                //adds each new port forwarding rule
                                for(NATEngineForwardingRule newRule : natEngineSetup.getNewForwardingRules())
                                {
                                    natEngine.addRedirect(newRule.getName(), newRule.getProtocol(), newRule.getHostIP(),
                                                          newRule.getHostPort(), newRule.getGuestIP(), newRule.getGuestPort());
                                }
                            }
                            else
                                ;
                        }
                        else
                            ;
                        //</editor-fold>

                        //<editor-fold defaultstate="collapsed" desc="Attachment Authorization">
                        boolean isAttachmentAuthorized = false;

                        switch(networksSetup[i].getAttachmentType())
                        {
                            case BRIDGED:
                            case HOST_ONLY:
                            {
                                try
                                {
                                    String networkName = networksSetup[i].getAttachedNetworkName();
                                    IHost host = vbox.getHost();
                                    String networkId = host.findHostNetworkInterfaceByName(networkName).getId();

                                    AuthorizationRequest permissionsRequest =
                                            new AuthorizationRequest(request.getRequestorId(),
                                                                     request.getServerId(),
                                                                     PermissionsConstants.ACTION_NETWORK_ATTACH,
                                                                     VirtualResourceType.NETWORK,
                                                                     networkId);

                                    isAttachmentAuthorized =
                                            permissionsManager.isActionAuthorized(permissionsRequest);
                                }
                                catch(WebServiceException e)
                                {
                                    opExceptions.add(Functions.parseVirtualBoxException(e));
                                }
                            } break;

                            case INTERNAL:
                            {
                                AuthorizationRequest permissionsRequest =
                                        new AuthorizationRequest(request.getRequestorId(),
                                                                 request.getServerId(),
                                                                 PermissionsConstants.ACTION_NETWORK_ATTACH,
                                                                 VirtualResourceType.NETWORK,
                                                                 networksSetup[i].getAttachedNetworkName());

                                isAttachmentAuthorized =
                                        permissionsManager.isActionAuthorized(permissionsRequest);
                            } break;

                            //its either NAT, VDE or a detachment, so action is authorized
                            default: isAttachmentAuthorized = true; break;
                        }
                        //</editor-fold>

                        //<editor-fold defaultstate="collapsed" desc="Network Attachment">
                        if(isAttachmentAuthorized)
                        {
                            //checks if the current network type is: internal
                            if(machineAdapter.getAttachmentType() == NetworkAttachmentType.INTERNAL)
                            {
                                //decreases the number of network interfaces attached to the internal network
                                databaseManager.decreaseNumberOfInternalNetworkAttachedNICs(
                                        machineAdapter.getInternalNetwork(), request.getServerId());
                            }
                            else
                                ;

                            switch(networksSetup[i].getAttachmentType())
                            {
                                case BRIDGED:
                                {
                                    machineAdapter.attachToBridgedInterface();
                                    machineAdapter.setHostInterface(networksSetup[i].getAttachedNetworkName());
                                } break;

                                case HOST_ONLY:
                                {
                                    machineAdapter.attachToHostOnlyInterface();
                                    machineAdapter.setHostInterface(networksSetup[i].getAttachedNetworkName());
                                } break;

                                case INTERNAL:
                                {
                                    machineAdapter.attachToInternalNetwork();
                                    machineAdapter.setInternalNetwork(networksSetup[i].getAttachedNetworkName());
                                    //increase the number of network interfaces attached to the internal network
                                    databaseManager.increaseNumberOfInternalNetworkAttachedNICs(
                                            networksSetup[i].getAttachedNetworkName(), request.getServerId());
                                } break;

                                case NAT:
                                {
                                    machineAdapter.attachToNAT();
                                    machineAdapter.setNATNetwork(networksSetup[i].getAttachedNetworkName());

                                    //nat engine settings should be changed later?
                                } break;

                                case VDE:
                                {
                                    machineAdapter.attachToVDE();
                                    machineAdapter.setVDENetwork(networksSetup[i].getAttachedNetworkName());
                                } break;

                                default:
                                {
                                    machineAdapter.detach();
                                    machineAdapter.setEnabled(false);
                                } break;
                            }
                        }
                        else
                            ;
                        //</editor-fold>
                    }
                    else
                        ;
                }
            }
            else
                ;
            //</editor-fold>

            //<editor-fold defaultstate="collapsed" desc="Shared Folders Settings">
            //checks if there are shared folders for removal
            ArrayList<String> foldersForRemoval = request.getSharedFoldersForRemoval();
            if(foldersForRemoval != null)
            {
                for(String folder : foldersForRemoval)
                {
                    try
                    {
                        //removes the shared folder
                        machine.removeSharedFolder(folder);
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;

            //checks if there are shared folders for addition
            ArrayList<SharedFolderSettings> sharedFolders = request.getNewSharedFolders();
            if(sharedFolders != null)
            {
                String sharedFoldersRoot = databaseManager.getServerSharedFoldersRoot(request.getServerId());
                for(SharedFolderSettings sharedFolder : sharedFolders)
                {
                    try
                    {
                        //creates the new shared folder
                        machine.createSharedFolder(sharedFolder.getName(),
                                                   sharedFoldersRoot + sharedFolder.getHostPath(),
                                                   sharedFolder.getIsWritable());
                    }
                    catch(WebServiceException e)
                    {
                        opExceptions.add(Functions.parseVirtualBoxException(e));
                    }
                }
            }
            else
                ;
            
            //</editor-fold>

            machine.saveSettings(); //saves the machine settings
            passedOp = true;
        }
        catch(WebServiceException e)
        {
            opExceptions.add(Functions.parseVirtualBoxException(e));
        }
        finally
        {
            try
            {
                if(machineSession != null)
                    machineSession.close(); //closes the machine sessions
                else
                    ;
            }catch(WebServiceException e){}
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, opExceptions);
    }

    /**
     * Mounts or unmounts a medium from a virtual machine.
     *
     * @param request medium mounting/unmounting request
     * @return a response to the machine management request
     */
    private MachineManagementResponse modifyVirtualMachineMountMedium(MountMediumRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;
        boolean openExistingSession = false; //if false -> local session is open, else existing is open

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox != null) //checks if the virtualbox object was retrieved
        {
            try
            {
                //attempts to retrieve the virtual machine
                IMachine machine = vbox.getMachine(request.getMachineId());

                //determines how to open the session to the machine
                SessionState machineSessionState = machine.getSessionState();
                if(machineSessionState == SessionState.OPEN || machineSessionState == SessionState.SPAWNING)
                    openExistingSession = true;
                else
                    openExistingSession = false;

                //checks if the mounting operation is allowed
                boolean isMediumMountAuthorized = false;
                if(request.getMountSettings().getMediumId() != null)
                {
                    isMediumMountAuthorized = permissionsManager.isActionAuthorized(
                                                    new AuthorizationRequest(request.getRequestorId(),
                                                                             request.getServerId(),
                                                                             PermissionsConstants.ACTION_MEDIUM_ATTACH,
                                                                             VirtualResourceType.MEDIUM,
                                                                             request.getMountSettings().getMediumId()));
                }
                else //unmounting is always allowed
                    isMediumMountAuthorized = true;

                if(isMediumMountAuthorized)
                    passedPreOp = true;
                else
                {
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_MEDIUM_ATTACHMENT_NOT_ALLOWED,
                                                              "(" + request.getMountSettings().getMediumId() + ")");
                }
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();
        ISession machineSession = null;

        try
        {
            //retrieves a machine session
            machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());

            //attempts to open the machine session
            if(openExistingSession)
                vbox.openExistingSession(machineSession, request.getMachineId());
            else
                vbox.openSession(machineSession, request.getMachineId());

            //retrieves the virtual machine object
            IMachine machine = machineSession.getMachine();

            //attempts t mount/unmount the medium
            MediumMountSettings mountSettings = request.getMountSettings();
            machine.mountMedium(mountSettings.getControllerName(),
                                mountSettings.getControllerPort(),
                                mountSettings.getControllerSlot(),
                                mountSettings.getMediumId(),
                                mountSettings.getForceMount());

            machine.saveSettings(); //saves the machine settings

            passedOp = true;
        }
        catch(WebServiceException e)
        {
            opExceptions.add(Functions.parseVirtualBoxException(e));
        }
        finally
        {
            try
            {
                if(machineSession != null)
                    machineSession.close(); //closes the machine sessions
                else
                    ;
            }catch(WebServiceException e){}
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, opExceptions);
    }

    /**
     * Changes the running state of a virtual machine.
     *
     * @param request machine change state request
     * @return a response to the machine management request
     */
    private MachineManagementResponse changeVirtualMachineState(ChangeMachineExecutionStateRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;
        ISession machineSession = null;

        //attemts to retrieve the virtualbox object and a machine reference for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());
        if(vbox != null && machineSession != null) //checks if the virtualbox and machine session objects was retrieved
        {
            try
            {
                //retrieves the virtual machine object
                IMachine machine = vbox.getMachine(request.getMachineId());
                MachineState machineState = machine.getState();
                boolean isRequestedStateValid = false;

                //checks if the machine is in a valid state, depending on the
                //requested change state action
                switch(request.getStateChangeAction())
                {
                    case POWER_UP:
                    {
                        if(machineState == MachineState.ABORTED 
                           || machineState == MachineState.POWERED_OFF 
                           || machineState == MachineState.SAVED)
                            isRequestedStateValid = true;
                        else
                            isRequestedStateValid = false;

                    } break;

                    case POWER_DOWN:
                    {
                        if(machineState != MachineState.RUNNING
                           && machineState != MachineState.PAUSED
                           && machineState != MachineState.STUCK)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    case SAVE:
                    {
                        if(machineState != MachineState.RUNNING
                           && machineState != MachineState.PAUSED)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    case RESET:
                    {
                        if(machineState != MachineState.RUNNING)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    case PAUSE:
                    {
                        if(machineState != MachineState.RUNNING)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    case RESUME:
                    {
                        if(machineState != MachineState.PAUSED)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    case ACPI_POWER:
                    {
                        if(machineState != MachineState.RUNNING)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    case ACPI_SLEEP:
                    {
                        if(machineState != MachineState.RUNNING)
                            isRequestedStateValid = false;
                        else
                            isRequestedStateValid = true;
                    } break;

                    default: isRequestedStateValid = false; break;
                }

                //if the current state of the machine is valid, attempts to open a remote session
                if(isRequestedStateValid)
                {
                    SessionState machineSessionState = machine.getSessionState();
                    if(machineSessionState == SessionState.OPEN || machineSessionState == SessionState.SPAWNING)
                    {
                        vbox.openExistingSession(machineSession, request.getMachineId());
                        passedPreOp = true;
                    }
                    else
                    {
                        IProgress progress = vbox.openRemoteSession(machineSession,
                                                                    request.getMachineId(),
                                                                    sessionType.toString(),
                                                                    null);

                        //waits for the operation to complete or for the maximum allowed time
                        progress.waitForCompletion(maxOperationWaitTime);

                        if(progress.getCompleted())
                        {
                            if(progress.getResultCode() == 0)
                                passedPreOp = true;
                            else
                            {
                                preOpException =
                                        new ApplicationException(ExceptionMessages.APP_E_MACHINE_STATE_CHANGE_FAILED,
                                                                 progress.getErrorInfo().getText());
                            }
                        }
                        else
                            ;//needs an expcetion condition
                    }
                }
                else
                {
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_REQUESTED_INVALID_STATE,
                                                              "(" + request.getStateChangeAction()
                                                              + ") for machine in state: (" + machineState + ")");
                }

            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;
        IProgress stateChangeProgress = null;

        try
        {
            //retrieves the machine console
            IConsole machineConsole = machineSession.getConsole();

            //executes the requested state change action
            switch(request.getStateChangeAction())
            {
                case POWER_UP: /* automatic power up... */; break;
                case POWER_DOWN: stateChangeProgress = machineConsole.powerDown(); break;
                case SAVE: stateChangeProgress = machineConsole.saveState(); break;
                case RESET: machineConsole.reset(); break;
                case PAUSE: machineConsole.pause(); break;
                case RESUME: machineConsole.resume(); break;
                case ACPI_POWER: machineConsole.powerButton(); break;
                case ACPI_SLEEP: machineConsole.sleepButton(); break;
                default: 
                {
                    opException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_REQUESTED_INVALID_STATE,
                                                           "(" + request.getStateChangeAction() + ")");
                } break;
            }

            if(opException == null)
                passedOp = true;
            else
                ;
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }
        finally
        {
            try
            {
                //closes the machine session
                machineSession.close();
            }
            catch(WebServiceException e)
            {
                //nothing can be done if an exception occurs here
            }
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, stateChangeProgress);
    }

    /**
     * Manages (create, remove, restore) the snapshots of a virtual machine.
     *
     * @param request
     * @return a response to the machine management request
     */
    private MachineManagementResponse manageVirtualMachineSnapshots(ManageMachineSnapshotsRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;
        ISession machineSession = null;

        //attemts to retrieve the virtualbox object and a machine session reference for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        machineSession = connectionsManager.getSessionReference(request.getServerId(), request.getRequestorId());
        if(vbox != null && machineSession != null) //checks if the virtualbox and machine session objects was retrieved
        {
            try
            {
                //retrieves the virtual machine object
                IMachine machine = vbox.getMachine(request.getMachineId());
                //retrieves the state of the machine
                MachineState machineState = machine.getState();

                //attempts to open the proper machine session
                SessionState machineSessionState = machine.getSessionState();
                if(machineSessionState == SessionState.OPEN || machineSessionState == SessionState.SPAWNING)
                    vbox.openExistingSession(machineSession, request.getMachineId());
                else
                {
                    if(machineState == MachineState.POWERED_OFF || machineState == MachineState.ABORTED || machineState == MachineState.SAVED)
                        vbox.openSession(machineSession, request.getMachineId());
                    else
                    {
                        IProgress progress = vbox.openRemoteSession(machineSession,
                                                                    request.getMachineId(),
                                                                    MachineRemoteSessionType.GUI.toString(),
                                                                    null);

                        //waits for the operation to complete or for the maximum allowed time
                        progress.waitForCompletion(maxOperationWaitTime);
                    }
                }

                passedPreOp = true;
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new MachineManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;
        IProgress snapshotOperationProgress = null;

        try
        {
            //retrieves the machine console object
            IConsole machineConsole = machineSession.getConsole();

            //determines what action to take
            switch(request.getSnapshotManagementAction())
            {
                //a new snapshot is to be created
                case CREATE:
                {
                    snapshotOperationProgress =
                            machineConsole.takeSnapshot(request.getSnapshotName(), request.getSnapshotDescription());
                    passedOp = true;
                } break;

                //a snapshot is to be modified
                case MODIFY:
                {
                    ISnapshot snapshot = machineConsole.getMachine().getSnapshot(request.getSnapshotId());
                    snapshot.setName(request.getSnapshotName());
                    snapshot.setDescription(request.getSnapshotDescription());
                    passedOp = true;

                    //save machine settings or do nothing?
                } break;

                //a snapshot is to be removed
                case REMOVE:
                {
                    snapshotOperationProgress = machineConsole.deleteSnapshot(request.getSnapshotId());
                    passedOp = true;
                } break;

                //a snapshot is to be restored
                case RESTORE:
                {
                    ISnapshot snapshot = machineConsole.getMachine().getSnapshot(request.getSnapshotId());
                    snapshotOperationProgress = machineConsole.restoreSnapshot(snapshot);
                    passedOp = true;
                } break;

                default: 
                {
                    opException = new ApplicationException(ExceptionMessages.APP_E_MACHINE_INVALID_SNAPSHOT_ACTION,
                                                           "(" + request.getSnapshotManagementAction() + ")");
                } break;
            }

            if(snapshotOperationProgress != null)
            {
                snapshotOperationProgress.waitForCompletion(maxOperationWaitTime);
                if(snapshotOperationProgress.getCompleted())
                {
                    if(snapshotOperationProgress.getResultCode() == 0)
                        passedPreOp = true;
                    else
                    {
                        preOpException =
                                new ApplicationException(ExceptionMessages.APP_E_MACHINE_SNAPSHOT_OPERATION_FAILED,
                                                         "(" + snapshotOperationProgress.getErrorInfo().getText() + ")");
                    }
                }
                else
                    ;//needs an expcetion condition
            }
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }
        finally
        {
            try
            {
                //closes the machine session
                machineSession.close();
            }
            catch(WebServiceException e)
            {
                //nothing can be done if an exception occurs here
            }
        }

        if(!passedOp)
            return new MachineManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new MachineManagementResponse(true, snapshotOperationProgress);
    }
}
